package common.aspect;
/**
 * @file:  MyApiLogAspect.java
 * @author: abel.zhan
 * @date:   2020/8/26 15:07
 * @copyright: 2020-2023 www.sd365.cn Inc. All rights reserved.
 */


import common.annotation.ApiLog;
import common.annotation.ApiLogOptions;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;

/**
 * @class MyApiLogAspect
 * @classdesc 实现ApiLog的功能，即打印请求日志和响应日志
 * v1.3.1 abel.zhan 2023-07-05 增加了版本兼容性处理，支持apiLog兼容分页开启功能
 * v1.3.2 able.zhan 2023-07-19 改进兼容方式增加ApiLog注解的选项，放弃1.3.1 的处理方式，不采用全局参数
 * @author chh
 * @date 2020/8/26  15:07
 * @version 1.0.0
 * @see
 * @since
 */
@Aspect
@Component
@Slf4j
public class ApiLogAspect {
    /**
     * @param: void
     * @return: void
     * @desc: 定义空方法用于切点表达式
     * @see
     * @since
     */
    @Pointcut("@annotation(common.annotation.ApiLog)")
    public void pointcut(){
    }

    /**
     *  接口日志的逻辑方便调试信息输出查看，旧接口包含分页开启方法，所以该接口做了兼容处理
     * @param: [joinPoint]
     * @return: java.lang.Object
     * @desc: 返回信息后，打印应答报文的日志 ，增加了对分页方法的判断，如果是分页方法则增加对分页
     * 对象的调用。
     * @see
     * @since
     */
    @Around("pointcut()")
    public Object printResponseDatagram(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("==> 开始执行方法" + joinPoint.getSignature().getName());
        Object result = null;
        // 方法执行前获取
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = getIpAddress(request);
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Object[] args = joinPoint.getArgs();
        long requestStartTime = System.currentTimeMillis();

        /**
         * 需要对旧系统的apilog注解的统一替换 @ApiLog(option=ApiLogOptions.INCLUDE_PAGINATION)
         * 如果在旧系统开发新模块使用@ApiLog注解则默认使用 Option=ONLY_LOG 的选项
         **/
        result = joinPoint.proceed();
        StringBuilder stringBuilder = new StringBuilder();
        long respTime = System.currentTimeMillis() - requestStartTime;
        String time = String.valueOf(respTime);
        log.info(stringBuilder.append("==> 拦截到请求\n")
                .append("==> 请求者工号：").append("\n")
                .append("==> 请求者IP：").append(ip).append("\n")
                .append("==> 请求时间：").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime())).append("\n")
                .append("==> 请求接口：").append(request.getMethod()).append(" ").append(request.getRequestURL()).append("\n")
                .append("==> 请求方法：").append(method.getName()).append("\n")
                .append("==> 参数内容：").append(Arrays.toString(args))
                .append("<== 请求耗时：").append(Double.parseDouble(time) / 1000).append("s\n")
                .append("<== 应答内容：").append(result)
                .toString()
        );
        return result;

    }

    /**
     * @param: [request]
     * @return: java.lang.String
     * @desc: 获取IP地址
     */
    private String getIpAddress(HttpServletRequest request){
        final String UNKNOWN = "unknown";
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}
